package com.jiangyao.common.configs;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import com.jiangyao.common.utils.StringUtils;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;

/**
 * redis配置
 *
 * @author: yangyongchuan
 * @create: 2020年06月05日 16:33
 */
@Configuration
public class RedisConfig {

	public void redisTemplateConfig(RedisTemplate template) {
		//使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
		//Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class);
		//使用Fastjson2JsonRedisSerializer来序列化和反序列化redis的value值 by zhengkai
		FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class);

		ObjectMapper mapper = new ObjectMapper();
		mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
		// 指定序列化输入的类型，类必须是非final修饰的，final修饰的类，比如String,Integer等会跑出异常
		mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
		serializer.setObjectMapper(mapper);

		template.setValueSerializer(serializer);
		//使用StringRedisSerializer来序列化和反序列化redis的key值
		template.setKeySerializer(new StringRedisSerializer());
		template.afterPropertiesSet();
	}

	@Value("${spring.redis.lettuce.pool.min-idle}")
	private Integer minIdle;
	@Value("${spring.redis.lettuce.pool.max-idle}")
	private Integer maxIdle;
	@Value("${spring.redis.lettuce.pool.max-active}")
	private Integer maxTotal;
	@Value("${spring.redis.lettuce.pool.max-wait}")
	private Long maxWaitMillis;


	@Bean("poolConfig")
	@ConfigurationProperties(prefix = "spring.redis.lettuce.pool")
	public GenericObjectPoolConfig config() {
		GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
		poolConfig.setMinIdle(minIdle);
		poolConfig.setMaxIdle(maxIdle);
		poolConfig.setMaxTotal(maxTotal);
		poolConfig.setMaxWaitMillis(maxWaitMillis);
		return poolConfig;
	}

	@Value("${spring.redis.hostName}")
	private String commonHostName;
	@Value("${spring.redis.port}")
	private Integer commonPort;
	@Value("${spring.redis.password}")
	private String commonPassword;
	@Value("${spring.redis.database}")
	private Integer commonDatabase;
	@Value("${spring.redis.timeout}")
	private Long commonTimeout;


	/**
	 * 配置数据源的:common
	 *
	 * @return
	 */
	@Bean("commonRedisConfig")
	@ConfigurationProperties(prefix = "spring.redis")
	public RedisStandaloneConfiguration commonRedisConfig() {
		RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration(commonHostName, commonPort);
		if (StringUtils.isNotBlank(commonPassword)) {
			configuration.setPassword(commonPassword);
		}
		if (commonDatabase != null && commonDatabase >= 0) {
			configuration.setDatabase(commonDatabase);
		}
		return configuration;
	}

	/**
	 * 配置第一个数据源的连接工厂
	 * 这里注意：需要添加@Primary 指定bean的名称，目的是为了创建两个不同名称的LettuceConnectionFactory
	 *
	 * @param config
	 * @param commonRedisConfig
	 * @return
	 */
	@Bean("commonFactory")
	@Primary
	public LettuceConnectionFactory commonFactory(@Qualifier("poolConfig") GenericObjectPoolConfig config, @Qualifier("commonRedisConfig") RedisStandaloneConfiguration commonRedisConfig) {
		LettuceClientConfiguration clientConfiguration = LettucePoolingClientConfiguration.builder().poolConfig(config).commandTimeout(Duration.ofMillis(commonTimeout)).build();
		LettuceConnectionFactory connectionFactory = new LettuceConnectionFactory(commonRedisConfig, clientConfiguration);
		connectionFactory.setShareNativeConnection(false);
		return connectionFactory;
	}


	/**
	 * 配置第一个数据源的RedisTemplate
	 * 注意：这里指定使用名称=factory 的 RedisConnectionFactory
	 * 并且标识第一个数据源是默认数据源 @Primary
	 *
	 * @param factory
	 * @return
	 */
	@Bean("redisTemplate")
	@Primary
	public RedisTemplate<String, Object> redisTemplate(@Qualifier("commonFactory") RedisConnectionFactory factory) {
		RedisTemplate<String, Object> template = new RedisTemplate<>();
		template.setConnectionFactory(factory);
		redisTemplateConfig(template);
		return template;
	}

}
